/*
 * CsvLoader.cpp
 *
 *  Created on: Oct 8, 2011
 *      Author: marcos
 */

#include "CsvLoader.h"
#include "../../util/NodoCandidato.h"
#include "../../util/NodoDistrito.h"
#include "../../util/NodoEleccion.h"
#include "../../util/NodoLista.h"
#include "../../util/NodoCargo.h"
#include "../../util/NodoVotante.h"
#include "../../util/NodoConteo.h"
using namespace std;


CsvLoader* CsvLoader::instance = NULL;

CsvLoader::CsvLoader() {
}

CsvLoader::~CsvLoader() {
}

CsvLoader* CsvLoader::getInstance() {
	if (instance == NULL) {
		instance = new CsvLoader();
	}
	return instance;
}

void CsvLoader::loadCsv(char command, string filePath) throw (CustomException, CsvFormatException) {

	ifstream textFile;
	textFile.open(filePath.c_str());

	if (textFile != NULL) {
		string firstLine;
		getline(textFile,firstLine);

		if (firstLine.compare("Distrito") == 0) {
			loadDistricts(command, textFile);
		}
		else {
			if (firstLine.compare("Eleccion") == 0) {
				loadElections(command, textFile);
			}
			else {
				if (firstLine.compare("Candidato") == 0) {
					loadCandidates(command, textFile);
				}
				else {
					if (firstLine.compare("Lista") == 0) {
						loadLists(command, textFile);
					}
					else {
						if (firstLine.compare("Cargo") == 0) {
							loadPositions(command, textFile);
						}
						else {
							if (firstLine.compare("Votante") == 0) {
								loadVoters(command, textFile);
							}
							else {
								if (firstLine.compare("Conteo") == 0) {
									loadCounters(command, textFile);
								}
								else {
									throw CustomException(CustomException::ERROR, "El archivo no tiene el formato correcto.");
								}
							}
						}
					}
				}
			}
		}
	}
	else {
		textFile.close();
		throw CustomException(CustomException::ERROR, "El archivo no existe.");
	}
	textFile.close();
}

void CsvLoader::loadDistricts(char command, ifstream& stream) {

	List* districts = new List();

	string currentLine;
	while (!stream.eof()) {
		getline(stream,currentLine);
		if (currentLine.find(",") < currentLine.length()) {
			int id = atoi(currentLine.substr(0,currentLine.find(",")).c_str());
			string name = currentLine.substr(currentLine.find(",")+1,currentLine.length());
			Distrito* district = Distrito::create(id, name);
			cout << "id: " << district->getId() << " nombre: " << district->getNombre() << "\n";

			NodoDistrito* districtNode = new NodoDistrito();
			districtNode->setNodeValue(district);
			districts->add(districtNode);
		}
		// puede ser una baja
		else {
			if (command == 'b' && currentLine.length() > 0) {
				cout << "ES UNA BAJA" << endl;
				int id = atoi(currentLine.c_str());
				Distrito* district = Distrito::create(id);
				NodoDistrito* districtNode = new NodoDistrito();
				districtNode->setNodeValue(district);
				districts->add(districtNode);
			}
		}
	}

	cout << "--------------------------------------------------------------------------------" << "\n";
	// Save the districts into the Tree
	tree::BPlusTree* tree= new tree::BPlusTree(DISTRITOS,BLOCKSIZE);

	for (int i=0; i<districts->getSize(); i++) {
		Distrito* distrito = ((NodoDistrito*) districts->get(i))->getNodeValue();

		if (command == 'b') {
			cout << "ID: " << distrito->getId() << endl;
			tree::Record* record = new tree::Record(distrito->getId());
			tree->remove(record);
		}
		else {
			stringstream ss(ios_base::in| ios_base::out| ios_base::binary);
			cout << "id: " << distrito->getId() << " nombre: " << distrito->getNombre() << "\n";
			distrito->toData(&ss);
			tree::Record* record = new tree::Record();
			record->edit(distrito->getId(), &ss);
			if (command == 'a') {
				tree->insert(record);
			}
			if (command == 'm') {
				tree->modify(record);
			}
		}
	}
	free(tree);
}

void CsvLoader::loadElections(char command, ifstream& stream) {

	string currentLine;
	List* elecciones = new List();

	while (!stream.eof()) {
		getline(stream,currentLine);
		if (currentLine.find(",") < currentLine.length()) {
			int id = atoi(currentLine.substr(0,currentLine.find(",")).c_str());
			currentLine = currentLine.substr(currentLine.find(",")+1,currentLine.length());
			int cargoId = atoi(currentLine.substr(0,currentLine.find(",")).c_str());
			currentLine = currentLine.substr(currentLine.find(",")+1,currentLine.length());
			int distritoId = atoi(currentLine.substr(0,currentLine.find(",")).c_str());
			string fecha = currentLine.substr(currentLine.find(",")+1,currentLine.length());

			Cargo* cargo = new Cargo();
			cargo->setId(cargoId);

			Distrito* distrito = new Distrito();
			distrito->setId(distritoId);

			Eleccion* eleccion = Eleccion::create(id, cargo, distrito, fecha);
			cout << "id: " << eleccion->getId() << " cargoId: " << eleccion->getCargo()->getId() << " distritoId: " << eleccion->getDistrito()->getId() << " fecha: " << eleccion->getFecha() << "\n";

			NodoEleccion* electionNode = new NodoEleccion();
			electionNode->setNodeValue(eleccion);
			elecciones->add(electionNode);
		}
		// puede ser una baja
		else {
			if (command == 'b' && currentLine.length() > 0) {
				cout << "ES UNA BAJA" << endl;
				int id = atoi(currentLine.c_str());
				Eleccion* eleccion = Eleccion::create(id);
				NodoEleccion* nodoEleccion = new NodoEleccion();
				nodoEleccion->setNodeValue(eleccion);
				elecciones->add(nodoEleccion);
			}
		}
	}

	cout << "--------------------------------------------------------------------------------" << "\n";
	// Save the districts into the Tree
	tree::BPlusTree * tree= new tree::BPlusTree(ELECCIONES,BLOCKSIZE);

	for (int i=0; i<elecciones->getSize(); i++) {

		Eleccion* eleccion = ((NodoEleccion*) elecciones->get(i))->getNodeValue();

		if (command == 'b') {
			cout << "ID: " << eleccion->getId() << endl;
			tree::Record* record = new tree::Record(eleccion->getId());
			tree->remove(record);
		}
		else {
			stringstream ss(ios_base::in| ios_base::out| ios_base::binary);
			cout << "id: " << eleccion->getId() << " cargoId: " << eleccion->getCargo()->getId() << " distritoId: " << eleccion->getDistrito()->getId() << " fecha: " << eleccion->getFecha() << "\n";
			eleccion->toData(&ss);
			tree::Record* record = new tree::Record();
			record->edit(eleccion->getId(), &ss);
			if (command == 'a') {
				tree->insert(record);
			}
			if (command == 'm') {
				tree->modify(record);
			}
		}
	}
	free(tree);
}

void CsvLoader::loadLists(char command, ifstream& stream) {

	string currentLine;
	List* listas = new List();

	while (!stream.eof()) {
		getline(stream,currentLine);
		if (currentLine.find(",") < currentLine.length()) {
			int id = atoi(currentLine.substr(0,currentLine.find(",")).c_str());
			currentLine = currentLine.substr(currentLine.find(",")+1,currentLine.length());

			int numero = atoi(currentLine.substr(0,currentLine.find(",")).c_str());
			currentLine = currentLine.substr(currentLine.find(",")+1,currentLine.length());

			string nombre = currentLine.substr(0,currentLine.find(",")).c_str();

			currentLine = currentLine.substr(currentLine.find(",")+1,currentLine.length());
			int eleccionId = atoi(currentLine.substr(0,currentLine.find(",")).c_str());
			Eleccion* e = new Eleccion();
			e->setId(eleccionId);

			currentLine = currentLine.substr(currentLine.find(",")+1,currentLine.length());
			int cantidadCandidatos = atoi(currentLine.substr(0,currentLine.find("{")).c_str());
			currentLine = currentLine.substr(currentLine.find("{")+1,currentLine.find_last_of("}"));

			List* candidatos = new List();

			for (int i=0; i<cantidadCandidatos; i++) {
				int candidatoId;
				if (i == cantidadCandidatos-1) {
					candidatoId = atoi(currentLine.c_str());
				}
				else {
					candidatoId = atoi(currentLine.substr(0,currentLine.find(",")).c_str());
					currentLine = currentLine.substr(currentLine.find(",")+1,currentLine.length());
				}

				Candidato* candidato = new Candidato();
				candidato->setId(candidatoId);
				NodoCandidato* nodoCandidato = new NodoCandidato();
				nodoCandidato->setNodeValue(candidato);
				candidatos->add(nodoCandidato);
			}

			Lista* lista = Lista::create(id, numero, nombre, e, candidatos);
			cout << "ID: " << lista->getId() << " Numero Lista: " << lista->getNumeroLista() << " Nombre: " << lista->getNombre() << " Eleccion ID: " << lista->getEleccion()->getId() << endl;
			for (int j=0; j<lista->getCandidatos()->getSize(); j++) {
				cout << "Candidato: " <<  ((NodoCandidato*)lista->getCandidatos()->get(j))->getNodeValue()->getId() << endl;
			}

			NodoLista* nodoLista = new NodoLista();
			nodoLista->setNodeValue(lista);
			listas->add(nodoLista);
		}
		// puede ser una baja
		else {
			if (command == 'b' && currentLine.length() > 0) {
				cout << "ES UNA BAJA" << endl;
				int id = atoi(currentLine.c_str());
				Lista* lista = Lista::create(id);
				NodoLista* nodoLista = new NodoLista();
				nodoLista->setNodeValue(lista);
				listas->add(nodoLista);
			}
		}

	}

	cout << "--------------------------------------------------------------------------------" << "\n";
	// Save the districts into the Tree
	tree::BPlusTree * tree= new tree::BPlusTree(LISTAS,BLOCKSIZE);

	for (int i=0; i<listas->getSize(); i++) {

		Lista* lista = ((NodoLista*) listas->get(i))->getNodeValue();

		if (command == 'b') {
			cout << "ID: " << lista->getId() << endl;
			tree::Record* record = new tree::Record(lista->getId());
			tree->remove(record);
		}
		else {
			stringstream ss(ios_base::in| ios_base::out| ios_base::binary);
			cout << "ID: " << lista->getId() << " Numero Lista: " << lista->getNumeroLista() << " Nombre: " << lista->getNombre() << " Eleccion ID: " << lista->getEleccion()->getId() << endl;
			for (int j=0; j<lista->getCandidatos()->getSize(); j++) {
				cout << "Candidato: " <<  ((NodoCandidato*)lista->getCandidatos()->get(j))->getNodeValue()->getId() << endl;
			}
			lista->toData(&ss);
			tree::Record* record = new tree::Record();
			record->edit(lista->getId(), &ss);
			if (command == 'a') {
				tree->insert(record);
			}
			if (command == 'm') {
				tree->modify(record);
			}
		}
	}
	free(tree);
}

void CsvLoader::loadCandidates(char command, ifstream& stream) {

	string currentLine;
	List* candidatos = new List();

	while (!stream.eof()) {
		getline(stream,currentLine);
		if (currentLine.find(",") < currentLine.length()) {
			int id = atoi(currentLine.substr(0,currentLine.find(",")).c_str());
			currentLine = currentLine.substr(currentLine.find(",")+1,currentLine.length());

			int dni = atoi(currentLine.substr(0,currentLine.find(",")).c_str());
			currentLine = currentLine.substr(currentLine.find(",")+1,currentLine.length());

			int listaId = atoi(currentLine.substr(0,currentLine.find(",")).c_str());
			currentLine = currentLine.substr(currentLine.find(",")+1,currentLine.length());

			int cargoId = atoi(currentLine.c_str());

			Votante* v = new Votante();
			v->setDni(dni);
			Lista* l = new Lista();
			l->setNumeroLista(listaId);
			Cargo* c = new Cargo();
			c->setId(cargoId);

			Candidato* candidato = Candidato::create(id, v, l, c);
			cout << "Id: " << candidato->getId() << " DNI: " << candidato->getVotante()->getDni() << " Lista ID: " << candidato->getLista()->getNumeroLista() << " Cargo ID: " << candidato->getCargo()->getId() << "\n";

			NodoCandidato* nodoCandidato = new NodoCandidato();
			nodoCandidato->setNodeValue(candidato);
			candidatos->add(nodoCandidato);
		}
		// puede ser una baja
		else {
			if (command == 'b' && currentLine.length() > 0) {
				cout << "ES UNA BAJA" << endl;
				int id = atoi(currentLine.c_str());
				Candidato* candidato = Candidato::create(id);
				NodoCandidato* candidatoNode = new NodoCandidato();
				candidatoNode->setNodeValue(candidato);
				candidatos->add(candidatoNode);
			}
		}
	}
	cout << "--------------------------------------------------------------------------------" << "\n";
	// Save the districts into the Tree
	tree::BPlusTree * tree= new tree::BPlusTree(CANDIDATOS,BLOCKSIZE);

	for (int i=0; i<candidatos->getSize(); i++) {
		Candidato* candidato = ((NodoCandidato*) candidatos->get(i))->getNodeValue();

		if (command == 'b') {
			cout << "ID: " << candidato->getId() << endl;
			tree::Record* record = new tree::Record(candidato->getId());
			tree->remove(record);
		}
		else {
			stringstream ss(ios_base::in| ios_base::out| ios_base::binary);
			cout << "Id: " << candidato->getId() << " DNI: " << candidato->getVotante()->getDni() << " Lista ID: " << candidato->getLista()->getNumeroLista() << " Cargo ID: " << candidato->getCargo()->getId() << "\n";
			candidato->toData(&ss);
			tree::Record* record = new tree::Record();
			record->edit(candidato->getId(), &ss);
			if (command == 'a') {
				tree->insert(record);
			}
			if (command == 'm') {
				tree->modify(record);
			}
		}
	}
	free(tree);
}

void CsvLoader::loadPositions(char command, ifstream& stream) {

	string currentLine;
	List* cargos = new List();

	while (!stream.eof()) {
		getline(stream,currentLine);
		if (currentLine.find(",") < currentLine.length()) {
			int id = atoi(currentLine.substr(0,currentLine.find(",")).c_str());
			string title = currentLine.substr(currentLine.find(",")+1,currentLine.length());

			Cargo* cargo = Cargo::create(id, title);
			cout << "Id: " << cargo->getId() << " Titulo: " << cargo->getTitulo() << "\n";

			NodoCargo* nodoCargo = new NodoCargo();
			nodoCargo->setNodeValue(cargo);
			cargos->add(nodoCargo);
		}
		// puede ser una baja
		else {
			if (command == 'b' && currentLine.length() > 0) {
				cout << "ES UNA BAJA" << endl;
				int id = atoi(currentLine.c_str());
				Cargo* cargo = Cargo::create(id);
				NodoCargo* cargoNode = new NodoCargo();
				cargoNode->setNodeValue(cargo);
				cargos->add(cargoNode);
			}
		}
	}
	cout << "--------------------------------------------------------------------------------" << "\n";
	// Save the districts into the Tree
	tree::BPlusTree * tree= new tree::BPlusTree(CARGOS,BLOCKSIZE);

	for (int i=0; i<cargos->getSize(); i++) {
		Cargo* cargo = ((NodoCargo*) cargos->get(i))->getNodeValue();

		if (command == 'b') {
			cout << "ID: " << cargo->getId() << endl;
			tree::Record* record = new tree::Record(cargo->getId());
			tree->remove(record);
		}
		else {
			stringstream ss(ios_base::in| ios_base::out| ios_base::binary);
			cout << "Id: " << cargo->getId() << " Titulo: " << cargo->getTitulo() << endl;
			cargo->toData(&ss);
			tree::Record* record = new tree::Record();
			record->edit(cargo->getId(), &ss);
			if (command == 'a') {
				tree->insert(record);
			}
			if (command == 'm') {
				tree->modify(record);
			}
		}
	}
	free(tree);
}

void CsvLoader::loadVoters(char command, ifstream& stream) {

	string currentLine;
	List* votantes = new List();

	while (!stream.eof()) {
		getline(stream,currentLine);
		if (currentLine.find(",") < currentLine.length()) {

			int dni = atoi(currentLine.substr(0,currentLine.find(",")).c_str());
			currentLine = currentLine.substr(currentLine.find(",")+1,currentLine.length());

			string nombre = currentLine.substr(0,currentLine.find(",")).c_str();
			currentLine = currentLine.substr(currentLine.find(",")+1,currentLine.length());
			string apellido = currentLine.substr(0,currentLine.find(",")).c_str();
			currentLine = currentLine.substr(currentLine.find(",")+1,currentLine.length());
			string password = currentLine.substr(0,currentLine.find(",")).c_str();
			currentLine = currentLine.substr(currentLine.find(",")+1,currentLine.length());

			int distritoId = atoi(currentLine.substr(currentLine.find(",")+1,currentLine.length()).c_str());

			Distrito* distrito = new Distrito();
			distrito->setId(distritoId);

			Votante* votante = Votante::create(dni,nombre,apellido,password,distrito,NULL);

			cout << "DNI: " << votante->getDni() << " Nombre: " << votante->getNombre() << " Apellido: " << votante->getApellido() << " Password: " << votante->getPassword() << " Distrito Id: " << votante->getDistrito()->getId() << endl;

			NodoVotante* nodoVotante = new NodoVotante();
			nodoVotante->setNodeValue(votante);
			votantes->add(nodoVotante);
		}
		// puede ser una baja
		else {
			if (command == 'b' && currentLine.length() > 0) {
				cout << "ES UNA BAJA" << endl;
				int dni = atoi(currentLine.c_str());
				Votante* votante = Votante::create(dni);
				NodoVotante* nodoVotante = new NodoVotante();
				nodoVotante->setNodeValue(votante);
				votantes->add(nodoVotante);
			}
		}
	}
	cout << "--------------------------------------------------------------------------------" << "\n";
	// Save the districts into the Tree
	tree::BPlusTree * tree= new tree::BPlusTree(VOTANTES,BLOCKSIZE);

	for (int i=0; i<votantes->getSize(); i++) {
		Votante* votante = ((NodoVotante*) votantes->get(i))->getNodeValue();

		if (command == 'b') {
			cout << "DNI: " << votante->getDni() << endl;
			tree::Record* record = new tree::Record(votante->getDni());
			tree->remove(record);
		}
		else {
			stringstream ss(ios_base::in| ios_base::out| ios_base::binary);
			cout << "DNI: " << votante->getDni() << " Nombre: " << votante->getNombre() << " Apellido: " << votante->getApellido() << " Password: " << votante->getPassword() << " Distrito Id: " << votante->getDistrito()->getId() << endl;
			tree::Record* record = new tree::Record();
			votante->toData(&ss);
			record->edit(votante->getDni(), &ss);

			if (command == 'a') {
				tree->insert(record);
			}
			if (command == 'm') {
				tree->modify(record);
			}
		}
	}

	cout << "LISTADO DE VOTANTES" << endl;
	tree::Record* record = tree->iterate();
	while(record != NULL){
		stringstream ss(ios_base::in| ios_base::out| ios_base::binary);
		ss.write(record->getData(), record->getDataSize());
		Votante* votante = Votante::toObject(&ss);
		cout << "DNI: " << votante->getDni() << " Nombre: " << votante->getNombre() << " Apellido: " << votante->getApellido() << " Password: " << votante->getPassword() << " Distrito Id: " << votante->getDistrito()->getId() << endl;
		record = tree->iterate();
	}

	free(tree);
}

void CsvLoader::loadCounters(char command, ifstream& stream) {

	List* conteos = new List();

	string currentLine;
	while (!stream.eof()) {
		getline(stream,currentLine);
		if (currentLine.find(",") < currentLine.length()) {
			int id = atoi(currentLine.substr(0,currentLine.find(",")).c_str());
			currentLine = currentLine.substr(currentLine.find(",")+1,currentLine.length());

			int listaId = atoi(currentLine.substr(0,currentLine.find(",")).c_str());
			currentLine = currentLine.substr(currentLine.find(",")+1,currentLine.length());
			Lista* lista = new Lista();
			lista->setId(listaId);

			int distritoId = atoi(currentLine.substr(0,currentLine.find(",")).c_str());
			currentLine = currentLine.substr(currentLine.find(",")+1,currentLine.length());
			Distrito* distrito = new Distrito();
			distrito->setId(distritoId);

			int eleccionId = atoi(currentLine.substr(0,currentLine.find(",")).c_str());
			currentLine = currentLine.substr(currentLine.find(",")+1,currentLine.length());
			Eleccion* eleccion = new Eleccion();
			eleccion->setId(eleccionId);

			int cantidad = atoi(currentLine.c_str());

			Conteo* conteo = Conteo::create(id, lista, distrito, eleccion, cantidad);
			cout << "id: " << conteo->getId() << endl;

			NodoConteo* conteoNode = new NodoConteo();
			conteoNode->setNodeValue(conteo);
			conteos->add(conteoNode);
		}
	}

	cout << "--------------------------------------------------------------------------------" << "\n";
	// Save the districts into the Tree
	tree::BPlusTree* tree= new tree::BPlusTree(CONTEOS,BLOCKSIZE);

	for (int i=0; i<conteos->getSize(); i++) {
		Conteo* conteo = ((NodoConteo*) conteos->get(i))->getNodeValue();

		stringstream ss(ios_base::in| ios_base::out| ios_base::binary);
		conteo->toData(&ss);
		tree::Record* record = new tree::Record();
		record->edit(conteo->getId(), &ss);
		tree->insert(record);
	}
	free(tree);
}
